package com.study.common.utils;

import cn.jiguang.common.connection.ApacheHttpClient;
import com.study.common.model.GaodeGpsResponse;
import com.study.common.model.GpsDataDto;
import com.study.common.model.WgGps;
import com.study.common.properties.GaodeProperties;
import com.study.common.utils.spring.SpringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * 各地图API坐标系统比较与转换;
 * WGS84坐标系：即地球坐标系，国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系,
 * 谷歌地图采用的是WGS84地理坐标系（中国范围除外）;
 * GCJ02坐标系：即火星坐标系，是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
 * 谷歌中国地图和搜搜中国地图采用的是GCJ02地理坐标系;
 * BD09坐标系：即百度坐标系，GCJ02坐标系经加密后的坐标系;
 * 搜狗坐标系、图吧坐标系等，估计也是在GCJ02基础上加密而成的。 chenhua
 */
public class PositionUtils {

    public static final String BAIDU_LBS_TYPE = "bd09ll";

    public static double pi = 3.1415926535897932384626;
    public static double a = 6378245.0;
    public static double ee = 0.00669342162296594323;

    /**
     * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
     *
     * @param lat
     * @param lon
     * @return
     */
    public static WgGps gps84_To_Gcj02(double lat, double lon) {
        if (outOfChina(lat, lon)) {
            //不是百度就还原
            return new WgGps(lat, lon);
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new WgGps(mgLat, mgLon);
    }

    /**
     * * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
     */
    public static WgGps gcj_To_Gps84(double lat, double lon) {
        WgGps gps = transform(lat, lon);
        double lontitude = lon * 2 - gps.getWgLon();
        double latitude = lat * 2 - gps.getWgLat();
        return new WgGps(latitude, lontitude);
    }

    /**
     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
     *
     * @param gg_lat
     * @param gg_lon
     */
    public static WgGps gcj02_To_Bd09(double gg_lat, double gg_lon) {
        double x = gg_lon, y = gg_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        return new WgGps(bd_lat, bd_lon);
    }

    /**
     * * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标 * * @param
     * bd_lat * @param bd_lon * @return
     */
    public static WgGps bd09_To_Gcj02(double bd_lat, double bd_lon) {
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * pi);
        double gg_lon = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new WgGps(gg_lat, gg_lon);
    }

    /**
     * (BD-09)-->84
     *
     * @param bd_lat
     * @param bd_lon
     * @return
     */
    public static WgGps bd09_To_Gps84(double bd_lat, double bd_lon) {
        WgGps gcj02 = bd09_To_Gcj02(bd_lat, bd_lon);
        WgGps map84 = gcj_To_Gps84(gcj02.getWgLat(),
                gcj02.getWgLon());
        return map84;

    }

    public static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347) {
            return true;
        }
        if (lat < 0.8293 || lat > 55.8271) {
            return true;
        }
        return false;
    }

    public static WgGps transform(double lat, double lon) {
        if (outOfChina(lat, lon)) {
            return new WgGps(lat, lon);
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new WgGps(mgLat, mgLon);
    }

    public static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
                + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    public static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
                * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
                * pi)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 直接转
     */
    public static WgGps gps84_To_Bd09(String lat, String lng) {
        WgGps gps = new WgGps(Double.parseDouble(lat), Double.parseDouble(lng));
        WgGps gcj = gps84_To_Gcj02(gps.getWgLat(), gps.getWgLon());
        WgGps bd = gcj02_To_Bd09(gcj.getWgLat(), gcj.getWgLon());
        return bd;
    }

    /**
     * 计算两个经纬度之间的距离
     *
     * @param latA latitudeA 第一个坐标的纬度
     * @param logA longtitudeA 第一个坐标的经度
     * @param latB latitudeB 第二个坐标的纬度
     * @param logB longtitudeB 第二个坐标的经度
     * @return 两个经纬度之间的距离
     */
    public static double distance(double latA, double logA, double latB, double logB) {
        if (latA == latB && logA == logB) {
            return 0;
        }
        int earthR = 6371000;
        double x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((logA - logB) * Math.PI / 180);
        double y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
        double s = x + y;
        if (s > 1) {
            s = 1;
        }
        if (s < -1) {
            s = -1;
        }
        double alpha = Math.acos(s);
        return alpha * earthR;
    }

    /**
     * @param gpsData1
     * @param gpsData2
     * @param isLast
     * @return List<GpsDataDto>
     * @Title: getPointsMotoDirection
     * @Description: 根据两点之间获取摩托车的路径规划
     * @author: 许智皓
     * @date: 2020年11月5日 下午3:51:27
     * @version: V1.0
     * @updateDate:
     * @update:
     */
    public List<GpsDataDto> getGaodePointsMotoDirection(List<GpsDataDto> trackPoints, GpsDataDto gpsData1,
                                                        GpsDataDto gpsData2, boolean isLast) {
        trackPoints = getGaodeCarDirection(trackPoints, gpsData1, gpsData2);
        if (checkArray(trackPoints)) {
            if (!isLast) {
                // 不是最后一包就去掉补录的最后一包
                trackPoints.remove(trackPoints.size() - 1);
            }
        }
        return trackPoints;
    }

    public static boolean checkArray(List array) {
        return null != array && 0 < array.size();
    }

    public static boolean checkStrArray(Object[] str) {
        return null != str && 0 < str.length;
    }

    public static boolean checkStr(String str) {
        if (null != str) {
            return !"".equals(str.trim()) && !"null".equals(str.trim());
        }
        return false;
    }

    /**
     * @param gpsData1
     * @param gpsData2 void
     * @Title: getCarDirection
     * @Description: 汽车- 路径规划
     * @author: 许智皓
     * @date: 2020年10月27日 下午3:48:16
     * @version: V1.0
     * @updateDate:
     * @update:
     */
    public List<GpsDataDto> getGaodeCarDirection(List<GpsDataDto> trackPoints, GpsDataDto gpsData1,
                                                 GpsDataDto gpsData2) {

        WgGps gps2 = gps84_To_Gcj02(gpsData2.getLatitude(), gpsData2.getLongitude());
        WgGps gps1 = gps84_To_Gcj02(gpsData1.getLatitude(), gpsData1.getLongitude());

        StringBuilder parameters = new StringBuilder();
        parameters.append("key=" + SpringUtils.getBean(GaodeProperties.class).getKey());
        parameters.append("&origin=" + gps1.getWgLon() + "," + gps1.getWgLat());
        parameters.append("&destination=" + gps2.getWgLon() + "," + gps2.getWgLat());
        parameters.append("&extensions=all&output=json");

        String path = "https://restapi.amap.com/v3/direction/driving?" + parameters.toString();

        GaodeGpsResponse res = null;

        try {
            ResponseEntity<GaodeGpsResponse> forEntity = SpringUtils.getBean(RestTemplate.class).getForEntity(path, GaodeGpsResponse.class);
            res = forEntity.getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (res != null && res.getStatus() == 1) {
            // 解析数据
            if (1 <= res.getCount()) {
                if (checkArray(res.getRoute().getPaths())) {
                    for (GaodeGpsResponse.GaodeMotoRcycleResult.GaodeMotoRcycleRoutes route : res.getRoute().getPaths()) {
                        if (checkArray(route.getSteps())) {
                            for (GaodeGpsResponse.GaodeMotoRcycleResult.GaodeMotoRcycleRoutes.GaodeMotoRcycleSteps step : route.getSteps()) {
                                if (checkArray(step.getTmcs())) {
                                    for (GaodeGpsResponse.GaodeMotoRcycleResult.GaodeMotoRcycleRoutes.GaodeMotoRcycleSteps.Tmcs tmcs : step.getTmcs()) {
                                        if (checkStr(tmcs.getPolyline())) {
                                            // 120.545410,31.311136;120.545010,31.311116;
                                            String[] paths = tmcs.getPolyline().split(";");
                                            if (checkStrArray(paths)) {
                                                for (String str : paths) {
                                                    String[] strs = str.split(",");
                                                    if (checkStrArray(strs) && 2 <= strs.length) {
                                                        try {
                                                            GpsDataDto point = new GpsDataDto();
                                                            //将高德地图经纬度转为gps经纬度
                                                            WgGps gps = gcj_To_Gps84(Double.parseDouble(strs[1]), Double.parseDouble(strs[0]));
                                                            point.setLongitude(gps.getWgLon());
                                                            point.setLatitude(gps.getWgLat());

                                                            point.setGpsTimestamp(gpsData1.getGpsTimestamp());
                                                            trackPoints.add(point);

                                                        } catch (Exception e) {
                                                            e.printStackTrace();
                                                        }

                                                    }

                                                }

                                            }

                                        }

                                    }

                                }

                            }
                        }
                    }
                }
            }
        }
        return trackPoints;
    }


}